/*
REWRITTEN FROM SCRATCH BY PUSSYWIZARD, IT OWNS NOW!
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "ScriptedGossip.h"
#include "ScriptedEscortAI.h"
#include "violet_hold.h"
#include "PassiveAI.h"
#include "SpellScript.h"
#include "Player.h"

/***********
** DEFENSE SYSTEM CRYSTAL
***********/

class go_vh_activation_crystal : public GameObjectScript
{
public:
	go_vh_activation_crystal() : GameObjectScript("go_vh_activation_crystal") { }

	bool OnGossipHello(Player* pPlayer, GameObject* pGo)
	{
		if (InstanceScript* pInstance = pGo->GetInstanceScript())
			pInstance->SetData(DATA_ACTIVATE_DEFENSE_SYSTEM, 1);
		return true;
	}
};

/***********
** SINCLARI
***********/

#define GOSSIP_START_EVENT  "Get your people to safety, we'll keep the Blue Dragonflight's forces at bay."
#define GOSSIP_ITEM_1	   "Activate the crystals when we get in trouble, right."
#define GOSSIP_I_WANT_IN	"Sorry, I'm late! Can I get in to help my friends?"

class npc_vh_sinclari : public CreatureScript
{
public:
	npc_vh_sinclari() : CreatureScript("npc_vh_sinclari") { }

	bool OnGossipHello(Player* pPlayer, Creature* pCreature)
	{
		if (InstanceScript* pInstance = pCreature->GetInstanceScript())
			switch (pInstance->GetData(DATA_ENCOUNTER_STATUS))
			{
				case NOT_STARTED:
					pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2);
					pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_START_EVENT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
					pPlayer->SEND_GOSSIP_MENU(13853, pCreature->GetGUID());
					break;
				case IN_PROGRESS:
					pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_I_WANT_IN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3);
					pPlayer->SEND_GOSSIP_MENU(13853, pCreature->GetGUID());
					break;
				default: // DONE or invalid
					pPlayer->SEND_GOSSIP_MENU(13910, pCreature->GetGUID());
			}
		return true;
	}

	bool OnGossipSelect(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction)
	{
		pPlayer->PlayerTalkClass->ClearMenus();
		switch(uiAction)
		{
			case GOSSIP_ACTION_INFO_DEF+1:
				pPlayer->CLOSE_GOSSIP_MENU();
				if (InstanceScript *pInstance = pCreature->GetInstanceScript())
					pInstance->SetData(DATA_START_INSTANCE, 1);
				break;
			case GOSSIP_ACTION_INFO_DEF+2:
				pPlayer->SEND_GOSSIP_MENU(13854, pCreature->GetGUID());
				break;
			case GOSSIP_ACTION_INFO_DEF+3:
				pPlayer->NearTeleportTo(playerTeleportPosition.GetPositionX(), playerTeleportPosition.GetPositionY(), playerTeleportPosition.GetPositionZ(), playerTeleportPosition.GetOrientation(), true);
				pPlayer->CLOSE_GOSSIP_MENU();
				break;
		}
		return true;
	}
};

/***********
** TELEPORTATION PORTAL
***********/

enum PortalEvents
{
	EVENT_SUMMON_KEEPER_OR_GUARDIAN = 1,
	EVENT_SUMMON_KEEPER_TRASH,
	EVENT_SUMMON_ELITES,
	EVENT_SUMMON_SABOTEOUR,
	EVENT_CHECK_DEATHS,
};

class npc_vh_teleportation_portal : public CreatureScript
{
public:
	npc_vh_teleportation_portal() : CreatureScript("npc_vh_teleportation_portal") { }

	CreatureAI* GetAI(Creature *pCreature) const
	{
		return new npc_vh_teleportation_portalAI(pCreature);
	}

	struct npc_vh_teleportation_portalAI : public NullCreatureAI
	{
		npc_vh_teleportation_portalAI(Creature *c) : NullCreatureAI(c), listOfMobs(me)
		{
			pInstance = c->GetInstanceScript();
			events.Reset();
			if (pInstance)
			{
				wave = pInstance->GetData(DATA_WAVE_COUNT);
				bKorG = false;
				spawned = false;

				if (wave < 12)
					addValue = 0;
				else
					addValue = 1;

				if (wave%6 != 0)
					events.RescheduleEvent(RAND(EVENT_SUMMON_KEEPER_OR_GUARDIAN,EVENT_SUMMON_ELITES), 10000);
				else
					events.RescheduleEvent(EVENT_SUMMON_SABOTEOUR, 3000);
			}
		}

		InstanceScript *pInstance;
		SummonList listOfMobs;
		EventMap events;
		uint8 wave;
		uint8 addValue;
		bool bKorG;
		bool spawned;

		void UpdateAI(uint32 diff)
		{
			if (!pInstance)
				return;

			events.Update(diff);

			switch(events.GetEvent())
			{
				case 0:
					break;
				case EVENT_SUMMON_KEEPER_OR_GUARDIAN:
					bKorG = true;
					spawned = true;
					if (Creature *c = DoSummon(RAND(NPC_PORTAL_GUARDIAN, NPC_PORTAL_KEEPER), me, 2.0f, 0, TEMPSUMMON_DEAD_DESPAWN))
						me->CastSpell(c, SPELL_PORTAL_CHANNEL, false);
					events.PopEvent();
					events.RescheduleEvent(EVENT_SUMMON_KEEPER_TRASH, 20000);
					break;
				case EVENT_SUMMON_KEEPER_TRASH:
					for (uint8 i=0; i<3+addValue; ++i)
					{
						uint32 entry = RAND(NPC_AZURE_INVADER_1, NPC_AZURE_INVADER_2, NPC_AZURE_SPELLBREAKER_1, NPC_AZURE_SPELLBREAKER_2, NPC_AZURE_MAGE_SLAYER_1, NPC_AZURE_MAGE_SLAYER_2, NPC_AZURE_BINDER_1, NPC_AZURE_BINDER_2);
						DoSummon(entry, me, 2.0f, 20000, TEMPSUMMON_DEAD_DESPAWN);
					}
					events.RepeatEvent(20000);
					break;
				case EVENT_SUMMON_ELITES:
					spawned = true;
					for (uint8 i=0; i<2+addValue; ++i)
					{
						uint32 entry = RAND(NPC_AZURE_CAPTAIN, NPC_AZURE_RAIDER, NPC_AZURE_STALKER, NPC_AZURE_SORCEROR);
						DoSummon(entry, me, 2.0f, 20000, TEMPSUMMON_DEAD_DESPAWN);
					}
					me->SetVisible(false);
					events.PopEvent();
					break;
				case EVENT_SUMMON_SABOTEOUR:
					DoSummon(NPC_SABOTEOUR, me, 2.0f, 0, TEMPSUMMON_CORPSE_DESPAWN);
					me->DespawnOrUnsummon(3000);
					events.PopEvent();
					break;
			}

			if (!spawned)
				return;

			if (bKorG)
			{
				if (!me->IsNonMeleeSpellCast(false)) // keeper/guardian died => channeled spell interrupted
				{
					// if keeper/guard lost all victims, in enterevademode linking aura is removed, restore it:
					if (pInstance)
						for (SummonList::iterator itr = listOfMobs.begin(); itr != listOfMobs.end(); ++itr)
							if (Creature* c = pInstance->instance->GetCreature(*itr))
								if (c->IsAlive() && (c->GetEntry() == NPC_PORTAL_GUARDIAN || c->GetEntry() == NPC_PORTAL_KEEPER))
								{
									me->CastSpell(c, SPELL_PORTAL_CHANNEL, false);
									return;
								}
					Unit::Kill(me, me, false);
				}
			}
			else
			{
				if (listOfMobs.empty())
					Unit::Kill(me, me, false);
			}
		}

		void JustDied(Unit* /*killer*/)
		{
			events.Reset();
			if (wave%6 == 0) // just to be sure, shouln't occur
				return;
			if (pInstance)
				pInstance->SetData(DATA_PORTAL_DEFEATED, 0);
		}

		void JustSummoned(Creature *pSummoned)
		{
			listOfMobs.Summon(pSummoned);
			if (pSummoned)
				pInstance->SetData64(DATA_ADD_TRASH_MOB, pSummoned->GetGUID());
		}

		void SummonedMobDied(Creature *pSummoned)
		{
			listOfMobs.Despawn(pSummoned);
			if (pSummoned)
				pInstance->SetData64(DATA_DELETE_TRASH_MOB, pSummoned->GetGUID());
		}
	};
};

/***********
** GENERAL TRASH AI
***********/

struct violet_hold_trashAI : public npc_escortAI
{
	violet_hold_trashAI(Creature *c) : npc_escortAI(c)
	{
		pInstance = c->GetInstanceScript();
		if (pInstance)
			PLoc = pInstance->GetData(DATA_PORTAL_LOCATION);
		bAddedWP = false;
		bAlt = false;
	}

	InstanceScript* pInstance;
	bool bAddedWP;
	uint32 PLoc;
	bool bAlt;

	void ClearDoorSealAura()
	{
		if (pInstance)
			if (Creature* c = pInstance->instance->GetCreature(pInstance->GetData64(DATA_DOOR_SEAL_GUID)))
				c->RemoveAura(SPELL_DESTROY_DOOR_SEAL, me->GetGUID());
	}

	void EnterCombat(Unit* who)
	{
		if (!who->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))
		{
			me->InterruptNonMeleeSpells(false);
			me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC);
		}
	}

	void AttackStart(Unit* who)
	{
		if (!who->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))
			ScriptedAI::AttackStart(who);
	}

	void JustReachedHome()
	{
		CreatureStartAttackDoor();
	}

	void WaypointReached(uint32 id)
	{
		if (PLoc<6)
			if (id == PLocWPCount[PLoc] -1 -(bAlt ? 1 : 0))
				CreatureStartAttackDoor();
	}

	void MoveInLineOfSight(Unit* who)
	{
		ScriptedAI::MoveInLineOfSight(who);
	}

	void UpdateAI(uint32 diff)
	{
		if (!bAddedWP)
		{
			bAddedWP = true;
			switch(PLoc)
			{
				case 0:
					for(int i=0;i<6;i++)
						AddWaypoint(i, FirstPortalTrashWPs[i][0]+irand(-1, 1), FirstPortalTrashWPs[i][1]+irand(-1, 1), FirstPortalTrashWPs[i][2]+irand(-1, 1), 0);
					me->SetHomePosition(FirstPortalTrashWPs[5][0], FirstPortalTrashWPs[5][1], FirstPortalTrashWPs[5][2], 3.149439f);
					break;
				case 1:
					bAlt = (bool)urand(0, 1);
					if (!bAlt)
					{
						for(int i=0;i<9;i++)
							AddWaypoint(i, SecondPortalTrashWPs1[i][0]+irand(-1, 1), SecondPortalTrashWPs1[i][1]+irand(-1, 1), SecondPortalTrashWPs1[i][2], 0);
						me->SetHomePosition(SecondPortalTrashWPs1[8][0]+irand(-1, 1), SecondPortalTrashWPs1[8][1]+irand(-1, 1), SecondPortalTrashWPs1[8][2]+irand(-1, 1), 3.149439f);
					}
					else
					{
						for(int i=0;i<8;i++)
							AddWaypoint(i, SecondPortalTrashWPs2[i][0]+irand(-1, 1), SecondPortalTrashWPs2[i][1]+irand(-1, 1), SecondPortalTrashWPs2[i][2], 0);
						me->SetHomePosition(SecondPortalTrashWPs2[7][0], SecondPortalTrashWPs2[7][1], SecondPortalTrashWPs2[7][2], 3.149439f);
					}
					break;
				case 2:
					for(int i=0;i<8;i++)
						AddWaypoint(i, ThirdPortalTrashWPs[i][0]+irand(-1, 1), ThirdPortalTrashWPs[i][1]+irand(-1, 1), ThirdPortalTrashWPs[i][2], 0);
					me->SetHomePosition(ThirdPortalTrashWPs[7][0], ThirdPortalTrashWPs[7][1], ThirdPortalTrashWPs[7][2], 3.149439f);
					break;
				case 3:
					for(int i=0;i<9;i++)
						AddWaypoint(i, FourthPortalTrashWPs[i][0]+irand(-1, 1), FourthPortalTrashWPs[i][1]+irand(-1, 1), FourthPortalTrashWPs[i][2], 0);
					me->SetHomePosition(FourthPortalTrashWPs[8][0], FourthPortalTrashWPs[8][1], FourthPortalTrashWPs[8][2], 3.149439f);
					break;
				case 4:
					for(int i=0;i<6;i++)
						AddWaypoint(i, FifthPortalTrashWPs[i][0]+irand(-1, 1), FifthPortalTrashWPs[i][1]+irand(-1, 1), FifthPortalTrashWPs[i][2], 0);
					me->SetHomePosition(FifthPortalTrashWPs[5][0], FifthPortalTrashWPs[5][1], FifthPortalTrashWPs[5][2], 3.149439f);
					break;
				case 5:
					for(int i=0;i<4;i++)
						AddWaypoint(i, SixthPoralTrashWPs[i][0]+irand(-1, 1), SixthPoralTrashWPs[i][1]+irand(-1, 1), SixthPoralTrashWPs[i][2], 0);
					me->SetHomePosition(SixthPoralTrashWPs[3][0], SixthPoralTrashWPs[3][1], SixthPoralTrashWPs[3][2], 3.149439f);
					break;
			}
			SetDespawnAtEnd(false);
			Start(true, true);
		}

		npc_escortAI::UpdateAI(diff);
	}

	void JustDied(Unit* /*unit*/)
	{
		if (pInstance)
			if (Creature* portal = ObjectAccessor::GetCreature((*me), pInstance->GetData64(DATA_TELEPORTATION_PORTAL_GUID)))
				CAST_AI(npc_vh_teleportation_portal::npc_vh_teleportation_portalAI, portal->AI())->SummonedMobDied(me);
	}

	void CreatureStartAttackDoor()
	{
		RemoveEscortState(STATE_ESCORT_ESCORTING | STATE_ESCORT_RETURNING | STATE_ESCORT_PAUSED);
		me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC);
		me->CastSpell((Unit*)NULL, SPELL_DESTROY_DOOR_SEAL, true);
	}

	void EnterEvadeMode()
	{
		if (!HasEscortState(STATE_ESCORT_ESCORTING))
		{
			me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC);
			me->SetHomePosition(1845.577759f+rand_norm()*5-2.5f, 800.681152f+rand_norm()*5-2.5f, 44.104248f, M_PI);
		}

		me->DeleteThreatList();
		me->CombatStop(true);
		if (HasEscortState(STATE_ESCORT_ESCORTING))
		{
			AddEscortState(STATE_ESCORT_RETURNING);
			ReturnToLastPoint();
		}
		else
		{
			me->GetMotionMaster()->MoveTargetedHome();
			Reset();
		}
		me->ClearUnitState(UNIT_STATE_EVADE);
	}
};

/***********
** TRASH SPELLS
***********/

enum AzureInvaderSpells
{	
	SPELL_CLEAVE = 15496,
	SPELL_IMPALE_N = 58459,
	SPELL_IMPALE_H = 59256,
	SPELL_BRUTAL_STRIKE = 58460,
	SPELL_SUNDER_ARMOR = 58461,
};
#define SPELL_IMPALE				DUNGEON_MODE(SPELL_IMPALE_N, SPELL_IMPALE_H)

enum AzureSpellbreakerSpells
{
	SPELL_ARCANE_BLAST_N = 58462,
	SPELL_ARCANE_BLAST_H = 59257,
	SPELL_SLOW = 25603,
	SPELL_CHAINS_OF_ICE = 58464,
	SPELL_CONE_OF_COLD_N = 58463,
	SPELL_CONE_OF_COLD_H = 59258
};
#define SPELL_ARCANE_BLAST			DUNGEON_MODE(SPELL_ARCANE_BLAST_N, SPELL_ARCANE_BLAST_H)
#define SPELL_CONE_OF_COLD			DUNGEON_MODE(SPELL_CONE_OF_COLD_N, SPELL_CONE_OF_COLD_H)

enum AzureBinderSpells
{
	SPELL_ARCANE_BARRAGE_N = 58456,
	SPELL_ARCANE_BARRAGE_H = 59248,
	SPELL_ARCANE_EXPLOSION_N = 58455,
	SPELL_ARCANE_EXPLOSION_H = 59245,
	SPELL_FROST_NOVA_N = 58458,
	SPELL_FROST_NOVA_H = 59253,
	SPELL_FROSTBOLT_N = 58457,
	SPELL_FROSTBOLT_H = 59251,
};
#define SPELL_ARCANE_BARRAGE		DUNGEON_MODE(SPELL_ARCANE_BARRAGE_N, SPELL_ARCANE_BARRAGE_H)
#define SPELL_ARCANE_EXPLOSION		DUNGEON_MODE(SPELL_ARCANE_EXPLOSION_N, SPELL_ARCANE_EXPLOSION_H)
#define SPELL_FROST_NOVA			DUNGEON_MODE(SPELL_FROST_NOVA_N, SPELL_FROST_NOVA_H)
#define SPELL_FROSTBOLT				DUNGEON_MODE(SPELL_FROSTBOLT_N, SPELL_FROSTBOLT_H)

enum AzureMageSlayerSpells
{
	SPELL_ARCANE_EMPOWERMENT = 58469,
	SPELL_SPELL_LOCK = 30849
};

enum AzureCaptainSpells
{
	SPELL_MORTAL_STRIKE = 32736,
	SPELL_WHIRLWIND_OF_STEEL = 41056
};

enum AzureSorcerorSpells
{
	SPELL_ARCANE_STREAM_N = 60181,
	SPELL_ARCANE_STREAM_H = 60204,
	SPELL_MANA_DETONATION_N = 60182,
	SPELL_MANA_DETONATION_H = 60205
};
#define SPELL_ARCANE_STREAM			DUNGEON_MODE(SPELL_ARCANE_STREAM_N, SPELL_ARCANE_STREAM_H)
#define SPELL_MANA_DETONATION		DUNGEON_MODE(SPELL_MANA_DETONATION_N, SPELL_MANA_DETONATION_H)

enum AzureRaiderSpells
{
	SPELL_CONCUSSION_BLOW = 52719,
	SPELL_MAGIC_REFLECTION = 60158
};

enum AzureStalkerSpells
{
	SPELL_BACKSTAB = 58471,
	SPELL_TACTICAL_BLINK = 58470
};

/***********
** TRASH
***********/

class npc_azure_invader : public CreatureScript
{
public:
	npc_azure_invader() : CreatureScript("npc_azure_invader") { }

	CreatureAI* GetAI(Creature* pCreature) const
	{
		return new npc_azure_invaderAI (pCreature);
	}

	struct npc_azure_invaderAI : public violet_hold_trashAI
	{
		npc_azure_invaderAI(Creature *c) : violet_hold_trashAI(c) {}

		uint32 uiCleaveTimer;
		uint32 uiImpaleTimer;
		uint32 uiBrutalStrikeTimer;
		uint32 uiSunderArmorTimer;

		void Reset()
		{
			uiCleaveTimer = 5000;
			uiImpaleTimer = 4000;
			uiBrutalStrikeTimer = 5000;
			uiSunderArmorTimer = 4000;
		}

		void UpdateAI(uint32 diff)
		{
			violet_hold_trashAI::UpdateAI(diff);

			if (!UpdateVictim())
				return;

			if (me->GetEntry() == NPC_AZURE_INVADER_1)
			{
				if (uiCleaveTimer <= diff)
				{
					DoCast(me->GetVictim(), SPELL_CLEAVE);
					uiCleaveTimer = 5000;
				} else uiCleaveTimer -= diff;

				if (uiImpaleTimer <= diff)
				{
					Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 5.0f, true);
					if (pTarget)
						DoCast(pTarget, SPELL_IMPALE);
					uiImpaleTimer = 4000;
				} else uiImpaleTimer -= diff;
			}

			if (me->GetEntry() == NPC_AZURE_INVADER_2)
			{
				if (uiBrutalStrikeTimer <= diff)
				{
					DoCast(me->GetVictim(), SPELL_BRUTAL_STRIKE);
					uiBrutalStrikeTimer = 5000;
				} else uiBrutalStrikeTimer -= diff;

				if (uiSunderArmorTimer <= diff)
				{
					DoCast(me->GetVictim(), SPELL_SUNDER_ARMOR);
					uiSunderArmorTimer = urand(8000, 10000);
				} else uiSunderArmorTimer -= diff;
			}

			DoMeleeAttackIfReady();
		}
	};
};



class npc_azure_binder : public CreatureScript
{
public:
	npc_azure_binder() : CreatureScript("npc_azure_binder") { }

	CreatureAI* GetAI(Creature* pCreature) const
	{
		return new npc_azure_binderAI (pCreature);
	}

	struct npc_azure_binderAI : public violet_hold_trashAI
	{
		npc_azure_binderAI(Creature *c) : violet_hold_trashAI(c) {}

		uint32 uiArcaneExplosionTimer;
		uint32 uiArcainBarrageTimer;
		uint32 uiFrostNovaTimer;
		uint32 uiFrostboltTimer;

		void Reset()
		{
			uiArcaneExplosionTimer = 5000;
			uiArcainBarrageTimer = 4000;
			uiFrostNovaTimer = 5000;
			uiFrostboltTimer = 4000;
		}

		void UpdateAI(uint32 diff)
		{
			violet_hold_trashAI::UpdateAI(diff);

			if (!UpdateVictim())
				return;

			if (me->GetEntry() == NPC_AZURE_BINDER_1)
			{
				if (uiArcaneExplosionTimer <= diff)
				{
					DoCast(SPELL_ARCANE_EXPLOSION);
					uiArcaneExplosionTimer = 5000;
				} else uiArcaneExplosionTimer -= diff;

				if (uiArcainBarrageTimer <= diff)
				{
					Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 30.0f, true);
					if (pTarget)
						DoCast(pTarget, SPELL_ARCANE_BARRAGE);
					uiArcainBarrageTimer = 6000;
				} else uiArcainBarrageTimer -= diff;
			}

			if (me->GetEntry() == NPC_AZURE_BINDER_2)
			{
				if (uiFrostNovaTimer <= diff)
				{
					DoCast(SPELL_FROST_NOVA);
					uiFrostNovaTimer = 5000;
				} else uiFrostNovaTimer -= diff;

				if (uiFrostboltTimer <= diff)
				{
					Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 40.0f, true);
					if (pTarget)
						DoCast(pTarget, SPELL_FROSTBOLT);
					uiFrostboltTimer = 6000;
				} else uiFrostboltTimer -= diff;
			}

			DoMeleeAttackIfReady();
		}
	};
};

class npc_azure_mage_slayer : public CreatureScript
{
public:
	npc_azure_mage_slayer() : CreatureScript("npc_azure_mage_slayer") { }

	CreatureAI* GetAI(Creature* pCreature) const
	{
		return new npc_azure_mage_slayerAI (pCreature);
	}

	struct npc_azure_mage_slayerAI : public violet_hold_trashAI
	{
		npc_azure_mage_slayerAI(Creature *c) : violet_hold_trashAI(c) {}

		uint32 uiArcaneEmpowermentTimer;
		uint32 uiSpellLockTimer;

		void Reset()
		{
			uiArcaneEmpowermentTimer = 5000;
			uiSpellLockTimer = 5000;
		}

		void UpdateAI(uint32 diff)
		{
			violet_hold_trashAI::UpdateAI(diff);

			if (!UpdateVictim())
				return;

			if (me->GetEntry() == NPC_AZURE_MAGE_SLAYER_1)
			{
				if (uiArcaneEmpowermentTimer <= diff)
				{
					DoCast(me, SPELL_ARCANE_EMPOWERMENT);
					uiArcaneEmpowermentTimer = 14000;
				} else uiArcaneEmpowermentTimer -= diff;
			}

			if (me->GetEntry() == NPC_AZURE_MAGE_SLAYER_2)
			{
				if (uiSpellLockTimer <= diff)
				{
					Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 30.0f, true);
					if (pTarget)
						DoCast(pTarget, SPELL_SPELL_LOCK);
					uiSpellLockTimer = 9000;
				} else uiSpellLockTimer -= diff;
			}

			DoMeleeAttackIfReady();
		}
	};
};

class npc_azure_raider : public CreatureScript
{
public:
	npc_azure_raider() : CreatureScript("npc_azure_raider") { }

	CreatureAI* GetAI(Creature* pCreature) const
	{
		return new npc_azure_raiderAI (pCreature);
	}

	struct npc_azure_raiderAI : public violet_hold_trashAI
	{
		npc_azure_raiderAI(Creature *c) : violet_hold_trashAI(c) {}

		uint32 uiConcussionBlowTimer;
		uint32 uiMagicReflectionTimer;

		void Reset()
		{
			uiConcussionBlowTimer = 5000;
			uiMagicReflectionTimer = 8000;
		}

		void UpdateAI(uint32 diff)
		{
			violet_hold_trashAI::UpdateAI(diff);

			if (!UpdateVictim())
				return;

			if (uiConcussionBlowTimer <= diff)
			{
				DoCast(me->GetVictim(), SPELL_CONCUSSION_BLOW);
				uiConcussionBlowTimer = 5000;
			} else uiConcussionBlowTimer -= diff;

			if (uiMagicReflectionTimer <= diff)
			{
				DoCast(SPELL_MAGIC_REFLECTION);
				uiMagicReflectionTimer = urand(10000, 15000);
			} else uiMagicReflectionTimer -= diff;

			DoMeleeAttackIfReady();
		}
	};
};

class npc_azure_stalker : public CreatureScript
{
public:
	npc_azure_stalker() : CreatureScript("npc_azure_stalker") { }

	CreatureAI* GetAI(Creature* pCreature) const
	{
		return new npc_azure_stalkerAI (pCreature);
	}

	struct npc_azure_stalkerAI : public violet_hold_trashAI
	{
		npc_azure_stalkerAI(Creature *c) : violet_hold_trashAI(c) {}

		uint32 uiBackstabTimer;
		uint32 uiTacticalBlinkTimer;
		bool TacticalBlinkCasted;

		void Reset()
		{
			uiBackstabTimer = 1300;
			uiTacticalBlinkTimer = 8000;
			TacticalBlinkCasted =false;
		}

		void UpdateAI(uint32 diff)
		{
			violet_hold_trashAI::UpdateAI(diff);

			if (!UpdateVictim())
				return;

			/*if (!TacticalBlinkCasted)
			{
				if (uiTacticalBlinkTimer <= diff)
				{
					Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 40.0f, true);
					if (pTarget)
						DoCast(pTarget, SPELL_TACTICAL_BLINK);
					uiTacticalBlinkTimer = 10000;
					TacticalBlinkCasted = true;
				} else uiTacticalBlinkTimer -= diff;
			}

			else*/
			{
				if (uiBackstabTimer <= diff)
				{
					Unit* pTarget = SelectTarget(SELECT_TARGET_NEAREST, 0, 5.0f, true);
					if (pTarget && !pTarget->HasInArc(M_PI, me))
						DoCast(pTarget, SPELL_BACKSTAB);
					TacticalBlinkCasted = false;
					uiBackstabTimer = 4000;
				} else uiBackstabTimer -= diff;
			}

			DoMeleeAttackIfReady();
		}
	};
};

class npc_azure_spellbreaker : public CreatureScript
{
public:
	npc_azure_spellbreaker() : CreatureScript("npc_azure_spellbreaker") { }

	CreatureAI* GetAI(Creature* pCreature) const
	{
		return new npc_azure_spellbreakerAI (pCreature);
	}

	struct npc_azure_spellbreakerAI : public violet_hold_trashAI
	{
		npc_azure_spellbreakerAI(Creature* c) : violet_hold_trashAI(c) {}

		uint32 uiArcaneBlastTimer;
		uint32 uiSlowTimer;
		uint32 uiChainsOfIceTimer;
		uint32 uiConeOfColdTimer;

		void Reset()
		{
			uiArcaneBlastTimer = 5000;
			uiSlowTimer = 4000;
			uiChainsOfIceTimer = 5000;
			uiConeOfColdTimer = 4000;
		}

		void UpdateAI(uint32 diff)
		{
			violet_hold_trashAI::UpdateAI(diff);

			if (!UpdateVictim())
				return;

			if (me->GetEntry() == NPC_AZURE_SPELLBREAKER_1)
			{
				if (uiArcaneBlastTimer <= diff)
				{
					Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 30.0f, true);
					if (pTarget)
						DoCast(pTarget, SPELL_ARCANE_BLAST);
					uiArcaneBlastTimer = 6000;
				} else uiArcaneBlastTimer -= diff;

				if (uiSlowTimer <= diff)
				{
					Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 30.0f, true);
					if (pTarget)
						DoCast(pTarget, SPELL_SLOW);
					uiSlowTimer = 5000;
				} else uiSlowTimer -= diff;
			}

			if (me->GetEntry() == NPC_AZURE_SPELLBREAKER_2)
			{
				if (uiChainsOfIceTimer <= diff)
				{
					Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 30.0f, true);
					if (pTarget)
						DoCast(pTarget, SPELL_CHAINS_OF_ICE);
					uiChainsOfIceTimer = 7000;
				} else uiChainsOfIceTimer -= diff;

				if (uiConeOfColdTimer <= diff)
				{
				   DoCast(SPELL_CONE_OF_COLD);
					uiConeOfColdTimer = 5000;
				} else uiConeOfColdTimer -= diff;
			}

			DoMeleeAttackIfReady();
		}
	};
};

class npc_azure_captain : public CreatureScript
{
public:
	npc_azure_captain() : CreatureScript("npc_azure_captain") { }

	CreatureAI* GetAI(Creature* pCreature) const
	{
		return new npc_azure_captainAI (pCreature);
	}

	struct  npc_azure_captainAI : public violet_hold_trashAI
	{
		npc_azure_captainAI(Creature *c) : violet_hold_trashAI(c) {}

		uint32 uiMortalStrikeTimer;
		uint32 uiWhirlwindTimer;

		void Reset()
		{
			uiMortalStrikeTimer = 5000;
			uiWhirlwindTimer = 8000;
		}

		void UpdateAI(uint32 diff)
		{
			violet_hold_trashAI::UpdateAI(diff);

			if (!UpdateVictim())
				return;

			if (uiMortalStrikeTimer <= diff)
			{
				DoCast(me->GetVictim(), SPELL_MORTAL_STRIKE);
				uiMortalStrikeTimer = 5000;
			} else uiMortalStrikeTimer -= diff;

			if (uiWhirlwindTimer <= diff)
			{
				DoCastAOE(SPELL_WHIRLWIND_OF_STEEL);
				uiWhirlwindTimer = 8000;
			} else uiWhirlwindTimer -= diff;

			DoMeleeAttackIfReady();
		}
	};
};

class npc_azure_sorceror : public CreatureScript
{
public:
	npc_azure_sorceror() : CreatureScript("npc_azure_sorceror") { }

	CreatureAI* GetAI(Creature* pCreature) const
	{
		return new npc_azure_sorcerorAI (pCreature);
	}

	struct  npc_azure_sorcerorAI : public violet_hold_trashAI
	{
		npc_azure_sorcerorAI(Creature *c) : violet_hold_trashAI(c) {}

		uint32 uiArcaneStreamTimer;
		uint32 uiArcaneStreamTimerStartingValueHolder;
		uint32 uiManaDetonationTimer;

		void Reset()
		{
			uiArcaneStreamTimer = 4000;
			uiArcaneStreamTimerStartingValueHolder = uiArcaneStreamTimer;
			uiManaDetonationTimer = 5000;
		}

		void UpdateAI(uint32 diff)
		{
			violet_hold_trashAI::UpdateAI(diff);

			if (!UpdateVictim())
				return;

			if (uiArcaneStreamTimer <= diff)
			{
				Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 35.0f, true);
				if (pTarget)
					DoCast(pTarget, SPELL_ARCANE_STREAM);
				uiArcaneStreamTimer = urand(0, 5000)+5000;
				uiArcaneStreamTimerStartingValueHolder = uiArcaneStreamTimer;
			} else uiArcaneStreamTimer -= diff;

			if (uiManaDetonationTimer <= diff && uiArcaneStreamTimer >=1500 && uiArcaneStreamTimer <= uiArcaneStreamTimerStartingValueHolder/2)
			{
				DoCastAOE(SPELL_MANA_DETONATION);
				uiManaDetonationTimer = urand(2000, 6000);
			} else uiManaDetonationTimer -= diff;

			DoMeleeAttackIfReady();
		}
	};
};

/***********
** SABOTEUR
***********/

enum AzureSaboteurSpells
{
	SABOTEUR_SHIELD_DISRUPTION = 58291,
	SABOTEUR_SHIELD_EFFECT = 45775,
	SPELL_TELEPORT_VISUAL = 52096,
};

class npc_azure_saboteur : public CreatureScript
{
public:
    npc_azure_saboteur() : CreatureScript("npc_azure_saboteur") { }

    CreatureAI* GetAI(Creature* pCreature) const
    {
        return new npc_azure_saboteurAI (pCreature);
    }

    struct npc_azure_saboteurAI : public npc_escortAI
    {
        npc_azure_saboteurAI(Creature *c) : npc_escortAI(c)
        {
            pInstance = c->GetInstanceScript();
			uiBoss = 0;
			if (pInstance)
				uiBoss = pInstance->GetData(DATA_WAVE_COUNT) == 6 ? pInstance->GetData(DATA_FIRST_BOSS_NUMBER) : pInstance->GetData(DATA_SECOND_BOSS_NUMBER);
            bAddedWPs = false;
			bOpening = false;
        }

        InstanceScript* pInstance;
        bool bAddedWPs;
		uint8 uiBoss;
		bool bOpening;
		uint32 timer;
		uint8 count;

        void WaypointReached(uint32 uiWPointId)
        {
			if (!pInstance)
				return;

            switch(uiBoss)
            {
                case 1:
                    if(uiWPointId == 2)
                        FinishPointReached();
                    break;
                case 2:
                    if(uiWPointId == 2)
                        FinishPointReached();
                    break;
                case 3:
                    if(uiWPointId == 1)
                        FinishPointReached();
                    break;
                case 4:
                    if(uiWPointId == 0)
                        FinishPointReached();
                    break;
                case 5:
                    if(uiWPointId == 0)
                        FinishPointReached();
                    break;
                case 6:
                    if(uiWPointId == 4)
                        FinishPointReached();
                    break;
            }
        }

        void UpdateAI(uint32 diff)
        {
            npc_escortAI::UpdateAI(diff);

            if(!bAddedWPs)
            {
                bAddedWPs = true;
                switch(uiBoss)
                {
                    case 1:
                        for(int i=0;i<3;i++)
                            AddWaypoint(i, SaboteurFinalPos1[i][0], SaboteurFinalPos1[i][1], SaboteurFinalPos1[i][2], 0);
                        me->SetHomePosition(SaboteurFinalPos1[2][0], SaboteurFinalPos1[2][1], SaboteurFinalPos1[2][2], 4.762346f);
                        break;
                    case 2:
                        for(int i=0;i<3;i++)
                            AddWaypoint(i, SaboteurFinalPos2[i][0], SaboteurFinalPos2[i][1], SaboteurFinalPos2[i][2], 0);
                        me->SetHomePosition(SaboteurFinalPos2[2][0], SaboteurFinalPos2[2][1], SaboteurFinalPos2[2][2], 1.862674f);
                        break;
                    case 3:
                        for(int i=0;i<2;i++)
                            AddWaypoint(i, SaboteurFinalPos3[i][0], SaboteurFinalPos3[i][1], SaboteurFinalPos3[i][2], 0);
                        me->SetHomePosition(SaboteurFinalPos3[1][0], SaboteurFinalPos3[1][1], SaboteurFinalPos3[1][2], 5.500638f);
                        break;
                    case 4:
                        AddWaypoint(0, SaboteurFinalPos4[0], SaboteurFinalPos4[1], SaboteurFinalPos4[2], 0);
                        me->SetHomePosition(SaboteurFinalPos4[0], SaboteurFinalPos4[1], SaboteurFinalPos4[2], 3.991108f);
                        break;
                    case 5:
                        AddWaypoint(0, SaboteurFinalPos5[0], SaboteurFinalPos5[1], SaboteurFinalPos5[2], 0);
                        me->SetHomePosition(SaboteurFinalPos5[0], SaboteurFinalPos5[1], SaboteurFinalPos5[2], 1.100841f);
                        break;
                    case 6:
                        for(int i=0;i<5;i++)
                            AddWaypoint(i, SaboteurFinalPos6[i][0], SaboteurFinalPos6[i][1], SaboteurFinalPos6[i][2], 0);
                        me->SetHomePosition(SaboteurFinalPos6[4][0], SaboteurFinalPos6[4][1], SaboteurFinalPos6[4][2], 0.983031f);
                        break;
                }
                SetDespawnAtEnd(false);
                Start(true, true);
            }

			if (bOpening)
			{
				if (timer <= diff)
				{
					if (count < 2)
					{
						me->CastSpell(me, SABOTEUR_SHIELD_DISRUPTION, false);
						timer = 1000;
					}
					else if (count == 2)
					{
						me->CastSpell(me, SABOTEUR_SHIELD_DISRUPTION, false);
						if (pInstance)
							pInstance->SetData(DATA_RELEASE_BOSS, 0);
						timer = 500;
					}
					else
					{
						bOpening = false;
						me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
						me->SetDisplayId(11686);
						me->CastSpell(me, SPELL_TELEPORT_VISUAL, true);
						me->DespawnOrUnsummon(1000);
					}
					++count;
				}
				else timer -= diff;
			}
        }

        void FinishPointReached()
        {
			bOpening = true;
			timer = 1000;
			count = 0;
            me->CastSpell(me, SABOTEUR_SHIELD_DISRUPTION, false);
        }

		void MoveInLineOfSight(Unit* /*who*/) {}
    };
};

/***********
** DESTROY DOOR SEAL SPELL SCRIPT
***********/

class spell_destroy_door_seal : public SpellScriptLoader
{
public:
	spell_destroy_door_seal() : SpellScriptLoader("spell_destroy_door_seal") { }

	class spell_destroy_door_sealAuraScript : public AuraScript
	{
		PrepareAuraScript(spell_destroy_door_sealAuraScript)

		void HandleEffectPeriodic(AuraEffect const * aurEff)
		{
			PreventDefaultAction();
			if (Unit* target = GetTarget())
				if (InstanceScript* pInstance = target->GetInstanceScript())
					pInstance->SetData(DATA_DECRASE_DOOR_HEALTH, 0);
		}

		void Register()
		{
			OnEffectPeriodic += AuraEffectPeriodicFn(spell_destroy_door_sealAuraScript::HandleEffectPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
		}
	};

	AuraScript *GetAuraScript() const
	{
		return new spell_destroy_door_sealAuraScript();
	}
};


class go_violet_hold_gate_lever : public GameObjectScript
{
public:
	go_violet_hold_gate_lever() : GameObjectScript("go_violet_hold_gate_lever") { }

	bool OnGossipHello(Player* plr, GameObject* go)
	{
		if (GameObject* gate = go->GetMap()->GetGameObject(MAKE_NEW_GUID(61606, 193019, HIGHGUID_GAMEOBJECT)))
			if (gate->getLootState() == GO_READY)
				gate->UseDoorOrButton(0, false, plr);
		return false;
	}
};

void AddSC_violet_hold()
{
	new go_vh_activation_crystal();
	new npc_vh_sinclari();
	new npc_vh_teleportation_portal();
	new npc_azure_saboteur();

	new npc_azure_invader();
	new npc_azure_spellbreaker();
	new npc_azure_binder();
	new npc_azure_mage_slayer();
	new npc_azure_captain();
	new npc_azure_sorceror();
	new npc_azure_raider();
	new npc_azure_stalker();

	new spell_destroy_door_seal();
	new go_violet_hold_gate_lever();
}
